/**
 * @file NEO_GPS.c
 * @brief NEO GPS模块驱动文件
 * @details 实现GPS数据的解析、时间同步、状态监控等功能
 * @author 开发者
 * @date 创建日期
 */

#include "NEO_GPS.h"

/**
 * @brief GPS数据解析函数
 * @param buff GPS原始数据缓冲区
 * @details 解析GNRMC语句，提取时间、状态、速度、日期等信息
 */
void get_gps_data(uint8_t *buff) {
#ifdef GPS_DEBUG
    printf("%s \n",buff);  // 调试模式下打印GPS原始数据
#endif
    
    /* 定义数据缓冲区 */
    uint8_t time[10] = {0};      // 时间缓冲区
    uint8_t status[10] = {0};    // 状态缓冲区
    uint8_t speed[10] = {0};     // 速度缓冲区
    uint8_t YMD[10] = {0};       // 日期缓冲区
    char * head_pt = NULL;       // 字符串解析指针
    char * last_pt = NULL;       // 字符串结束指针

    uint8_t i;
    // 找到GNRMC语句开头
    head_pt = strstr((char *)buff,"GNRMC");
    if (head_pt == NULL) {
#ifdef GPS_DEBUG
        printf("[GPS_INFO] GNRMC not found.\n");  // 调试信息：未找到GNRMC语句
#endif
        return;
    }
    
    static int GPS_Stust = 1;    // GPS状态标志，1表示连接，0表示未连接
    // 取出GNRMC后面九个字段的值
    GPS_Stust_SHOW(GPS_Stust);   // 更新GPS状态显示

    /* 解析GNRMC语句的各个字段 */
    for(i = 0; i < 9; i++) {
        if(i == 0) {
            last_pt = strstr(head_pt,",");  // 找到第一个逗号
            if (last_pt == NULL) {
                printf("[GPS_INFO] , not found.\n");
                return;
            }
            head_pt = last_pt;
            head_pt += 1;  // 移动到下一个字段
            continue;
        }
        last_pt = strstr(head_pt,",");  // 找到下一个逗号
        if(last_pt == NULL) {
#ifdef GPS_DEBUG
            printf("[GPS_INFO] , not found.\n");
#endif
            return;
        }
        
        /* 根据字段位置提取相应数据 */
        switch(i) {
        case 1:
            // 获取时分秒
            memcpy(time,head_pt,last_pt - head_pt);
            break;
        case 2:
            // 获取GPS状态
            memcpy(status,head_pt,last_pt - head_pt);
            if(status[0] ==  'V') {
                GPS_Stust = 0;  // V表示无效数据，GPS未连接
#ifdef GPS_DEBUG
                printf("[GPS_INFO] GPS NOT LINK");
#endif
                return;
            } else {
                GPS_Stust = 1;  // A表示有效数据，GPS已连接
            }
            break;
        case 7:
            // 获取速度(节)，传入数据处理函数
            memcpy(speed,head_pt,last_pt - head_pt);
            get_speed(speed);
            break;
        case 9:
            // 获取年月日
            memcpy(YMD,head_pt,last_pt - head_pt);
            break;
        }
        head_pt = last_pt + 1;  // 移动到下一个字段
    }
#ifdef GPS_DEBUG
    printf("time is %s \n", time);    // 打印解析出的时间
    printf("status is %s \n", status); // 打印解析出的状态
#endif
    
    // 传值进函数取出时分秒年月日时间
    // 静态变量只会初始化一次，进入函数后会变成1
    static int set_rtc = 0;
    // 第一次进入时RTC时钟开始同步
    if(set_rtc == 0 ) {
        get_gps_time(time,YMD);  // 同步GPS时间到RTC
        set_rtc = 1;
    }
}




/**
 * @brief GPS时间解析和RTC同步函数
 * @param HMSBuff 时分秒数据缓冲区
 * @param YMD 年月日数据缓冲区
 * @details 解析GPS时间数据，进行时区转换，并同步到RTC时钟
 */
void get_gps_time(uint8_t *HMSBuff, uint8_t * YMD)
{
    // 判断传入数组是否为空
    if(HMSBuff == NULL) {
        return;
    }
    if(YMD == NULL) {
        return;
    }
    
    /* 定义年月日时分秒变量 */
    int Year = 0;
    int Month = 0;
    int Day = 0;
    int Hour = 0;
    int Minute = 0;
    int Second = 0;
    
    // 取出时分秒
    sscanf((char *)HMSBuff, "%2d%2d%2d", &Hour, &Minute, &Second);
    // 取出年月日
    sscanf((char *)YMD, "%2d%2d%2d", &Day, &Month, &Year);
    
    Hour += TIMEZONE_OFFSET;  // UTC时间+8小时转换为北京时间
    
    // UTC时间+8，如果大于24则减去24
    if (Hour >= 24) {
        Hour -= 24;  // 如果小时数超过24，减去24
        Day += 1;    // 进位到下一天
    }
    
    /* 处理日期进位 */
    int days_in_month[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    if (Month == 2 && ((Year % 4 == 0 && Year % 100 != 0) || (Year % 400 == 0))) {
        days_in_month[1] = 29;  // 闰年的2月有29天
    }
    if (Day > days_in_month[Month - 1]) {
        Day = 1;     // 日期进位到下一个月的第一天
        Month += 1;  // 进位到下一个月
    }
    if (Month > 12) {
        Month = 1;   // 月份进位到下一年的1月
        Year += 1;   // 进位到下一年
    }
#ifdef GPS_DEBUG
    printf("h is %d \n", Hour);
    printf("m is %d \n", Minute);
    printf("s is %d \n", Second);
    printf("d is %d \n", Day);
    printf("m is %d \n", Month);
    printf("y is %d \n", Year + 2000);
#endif

    /* 配置RTC时钟 */
    rcu_periph_clock_enable(RCU_PMU);  // 使能电源管理单元时钟
    pmu_backup_write_enable();         // 使能备份域写操作
    
    rtc_parameter_struct rps;          // RTC参数结构体
    rps.year = WRITE_BCD(Year);        // 设置年份（BCD格式）
    rps.month = WRITE_BCD(Month);      // 设置月份（BCD格式）
    rps.date = WRITE_BCD(Day);         // 设置日期（BCD格式）
    rps.day_of_week = 0x04;            // 设置星期（周四）
    rps.hour = WRITE_BCD(Hour);        // 设置小时（BCD格式）
    rps.minute = WRITE_BCD(Minute);    // 设置分钟（BCD格式）
    rps.second = WRITE_BCD(Second);    // 设置秒（BCD格式）
    rps.display_format = RTC_24HOUR;   // 24小时制显示格式
    rps.am_pm = RTC_PM;                // 下午

    rps.factor_asyn = 127;   // 7位异步预分频器，0x0 - 0x7F
    rps.factor_syn  = 249;   // 15位同步预分频器，0x0 - 0x7FFF
    rtc_init(&rps);          // 初始化RTC
}

/* 外部队列句柄声明 */
extern QueueHandle_t GPS_Stust;

/**
 * @brief GPS状态显示函数
 * @param Stust GPS状态值
 * @details 根据GPS状态变化，通过队列发送状态更新消息
 */
void GPS_Stust_SHOW(int Stust) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;  // 高优先级任务唤醒标志
    static uint8_t num = 0;                         // 发送的状态值
    static int Draw_index = 1;                      // 绘制索引标志
    
    /* GPS未连接状态处理 */
    if(Stust == 0 && Draw_index == 0) {
#ifdef release
        num = 20;  // 发布版本发送状态20
#endif
#ifdef DEBUG
        num = 30;  // 调试版本发送状态30
#endif
        xQueueSendFromISR( GPS_Stust, &num, &xHigherPriorityTaskWoken );  // 从ISR发送队列
        if( xHigherPriorityTaskWoken ) {
            /* 实际使用的宏是端口特定的 */
            portYIELD_FROM_ISR (xHigherPriorityTaskWoken);  // 从ISR进行任务切换
        }
        Draw_index = 1;
    }
    
    /* GPS连接状态处理 */
    if(Stust == 1 && Draw_index == 1) {
#ifdef release
        num = 30;  // 发布版本发送状态30
#endif
        xQueueSendFromISR( GPS_Stust, &num, &xHigherPriorityTaskWoken );  // 从ISR发送队列
        if( xHigherPriorityTaskWoken ) {
            /* 实际使用的宏是端口特定的 */
            portYIELD_FROM_ISR (xHigherPriorityTaskWoken);  // 从ISR进行任务切换
        }
        Draw_index = 0;
    }
}

